<?php
/*~ class.phpmailer-lite.php
.---------------------------------------------------------------------------.
|  software: phpmailer lite - php email class                               |
|   version: 5.1                                                            |
|   contact: via sourceforge.net support pages (also www.codeworxtech.com)  |
|      info: http://phpmailer.sourceforge.net                               |
|   support: http://sourceforge.net/projects/phpmailer/                     |
| ------------------------------------------------------------------------- |
|     admin: andy prevost (project admininistrator)                         |
|   authors: andy prevost (codeworxtech) codeworxtech@users.sourceforge.net |
|          : marcus bointon (coolbru) coolbru@users.sourceforge.net         |
|   founder: brent r. matzelle (original founder)                           |
| copyright (c) 2004-2009, andy prevost. all rights reserved.               |
| copyright (c) 2001-2003, brent r. matzelle                                |
| ------------------------------------------------------------------------- |
|   license: distributed under the lesser general public license (lgpl)     |
|            http://www.gnu.org/copyleft/lesser.html                        |
| this program is distributed in the hope that it will be useful - without  |
| any warranty; without even the implied warranty of merchantability or     |
| fitness for a particular purpose.                                         |
| ------------------------------------------------------------------------- |
| we offer a number of paid services (www.codeworxtech.com):                |
| - web hosting on highly optimized fast and secure servers                 |
| - technology consulting                                                   |
| - oursourcing (highly qualified programmers and graphic designers)        |
'---------------------------------------------------------------------------'
*/

/**
 * phpmailer lite - php email transport class
 * note: requires php version 5 or later
 * @package phpmailer lite
 * @author andy prevost
 * @author marcus bointon
 * @copyright 2004 - 2009 andy prevost
 * @version $id: class.phpmailer-lite.php 447 2009-09-12 13:21:38z codeworxtech $
 * @license http://www.gnu.org/copyleft/lesser.html gnu lesser general public license
 */

class postman
{
	
	/////////////////////////////////////////////////
	// properties, public
	/////////////////////////////////////////////////
	

	/**
	 * email priority (1 = high, 3 = normal, 5 = low).
	 * @var int
	 */
	public $priority = 3;
	
	/**
	 * sets the charset of the message.
	 * @var string
	 */
	public $charset = 'utf-8';
	
	/**
	 * sets the content-type of the message.
	 * @var string
	 */
	public $contenttype = 'text/plain';
	
	/**
	 * sets the encoding of the message. options for this are
	 * "8bit", "7bit", "binary", "base64", and "quoted-printable".
	 * @var string
	 */
	public $encoding = '8bit';
	
	/**
	 * holds the most recent mailer error message.
	 * @var string
	 */
	public $errorinfo = '';
	
	/**
	 * sets the from email address for the message.
	 * @var string
	 */
	public $from = 'root@localhost';
	
	/**
	 * sets the from name of the message.
	 * @var string
	 */
	public $fromname = 'root user';
	
	/**
	 * sets the sender email (return-path) of the message.  if not empty,
	 * will be sent via -f to sendmail
	 * @var string
	 */
	public $sender = '';
	
	/**
	 * sets the subject of the message.
	 * @var string
	 */
	public $subject = '';
	
	/**
	 * sets the body of the message.  this can be either an html or text body.
	 * if html then run ishtml(true).
	 * @var string
	 */
	public $body = '';
	
	/**
	 * sets the text-only body of the message.  this automatically sets the
	 * email to multipart/alternative.  this body can be read by mail
	 * clients that do not have html email capability such as mutt. clients
	 * that can read html will view the normal body.
	 * @var string
	 */
	public $altbody = '';
	
	/**
	 * sets word wrapping on the body of the message to a given number of
	 * characters.
	 * @var int
	 */
	public $wordwrap = 0;
	
	/**
	 * method to send mail: ("mail", or "sendmail").
	 * @var string
	 */
	public $mailer = 'mail';
	
	/**
	 * sets the path of the sendmail program.
	 * @var string
	 */
	public $sendmail = '/usr/sbin/sendmail';
	
	/**
	 * sets the email address that a reading confirmation will be sent.
	 * @var string
	 */
	public $confirmreadingto = '';
	
	/**
	 * sets the hostname to use in message-id and received headers
	 * and as default helo string. if empty, the value returned
	 * by server_name is used or 'localhost.localdomain'.
	 * @var string
	 */
	public $hostname = '';
	
	/**
	 * sets the message id to be used in the message-id header.
	 * if empty, a unique id will be generated.
	 * @var string
	 */
	public $messageid = '';
	
	/**
	 * provides the ability to have the to field process individual
	 * emails, instead of sending to entire to addresses
	 * @var bool
	 */
	public $singleto = true;
	
	/**
	 * if singleto is true, this provides the array to hold the email addresses
	 * @var bool
	 */
	public $singletoarray = array();
	
	/**
	 * provides the ability to change the line ending
	 * @var string
	 */
	public $le = "\n";
	
	/**
	 * used with dkim dns resource record
	 * @var string
	 */
	public $dkim_selector = 'postman';
	
	/**
	 * used with dkim dns resource record
	 * optional, in format of email address 'you@yourdomain.com'
	 * @var string
	 */
	public $dkim_identity = '';
	
	/**
	 * used with dkim dns resource record
	 * required, in format of base domain 'yourdomain.com'
	 * @var string
	 */
	public $dkim_domain = '';
	
	/**
	 * used with dkim digital signing process
	 * optional
	 * @var string
	 */
	public $dkim_passphrase = '';
	
	/**
	 * used with dkim dns resource record
	 * required, private key (read from /.htprivkey)
	 * @var string
	 */
	public $dkim_private = '';
	
	/**
	 * callback action function name
	 * the function that handles the result of the send email action. parameters:
	 * bool    $result        result of the send action
	 * string  $to            email address of the recipient
	 * string  $cc            cc email addresses
	 * string  $bcc           bcc email addresses
	 * string  $subject       the subject
	 * string  $body          the email body
	 * @var string
	 */
	public $action_function = ''; //'callbackaction';
	

	/**
	 * sets the phpmailer version number
	 * @var string
	 */
	public $version = '';
	
	public $vars = array();
	public $tpl_path = '';
	
	/////////////////////////////////////////////////
	// properties, private and protected
	/////////////////////////////////////////////////
	

	private $to = array();
	private $cc = array();
	private $bcc = array();
	private $replyto = array();
	private $all_recipients = array();
	private $attachment = array();
	private $customheader = array();
	private $message_type = '';
	private $boundary = array();
	protected $language = array();
	private $error_count = 0;
	private $sign_cert_file = "";
	private $sign_key_file = "";
	private $sign_key_pass = "";
	private $exceptions = false;
	
	/////////////////////////////////////////////////
	// constants
	/////////////////////////////////////////////////
	

	const stop_message = 0; // message only, continue processing
	const stop_continue = 1; // message?, likely ok to continue processing
	const stop_critical = 2; // message, plus full stop, critical error reached
	

	/////////////////////////////////////////////////
	// methods, variables
	/////////////////////////////////////////////////
	

	/**
	 * constructor
	 * @param boolean $exceptions should we throw external exceptions?
	 */
	public function __construct($exceptions = false)
	{
		$this->exceptions = ($exceptions == true);
	}
	
	/**
	 * sets message type to html.
	 * @param bool $ishtml
	 * @return void
	 */
	public function ishtml($ishtml = true)
	{
		if ($ishtml)
		{
			$this->contenttype = 'text/html';
		}
		else
		{
			$this->contenttype = 'text/plain';
		}
	}
	
	/**
	 * sets mailer to send message using php mail() function.
	 * @return void
	 */
	public function ismail()
	{
		$this->mailer = 'mail';
	}
	
	/**
	 * sets mailer to send message using the $sendmail program.
	 * @return void
	 */
	public function issendmail()
	{
		if (! stristr(ini_get('sendmail_path'), 'sendmail'))
		{
			$this->sendmail = '/var/qmail/bin/sendmail';
		}
		$this->mailer = 'sendmail';
	}
	
	/**
	 * sets mailer to send message using the qmail mta.
	 * @return void
	 */
	public function isqmail()
	{
		if (stristr(ini_get('sendmail_path'), 'qmail'))
		{
			$this->sendmail = '/var/qmail/bin/sendmail';
		}
		$this->mailer = 'sendmail';
	}
	
	/////////////////////////////////////////////////
	// methods, recipients
	/////////////////////////////////////////////////
	

	/**
	 * adds a "to" address.
	 * @param string $address
	 * @param string $name
	 * @return boolean true on success, false if address already used
	 */
	public function addaddress($address, $name = '')
	{
		return $this->addanaddress('to', $address, $name);
	}
	
	/**
	 * adds a "cc" address.
	 * note: this function works with the smtp mailer on win32, not with the "mail" mailer.
	 * @param string $address
	 * @param string $name
	 * @return boolean true on success, false if address already used
	 */
	public function addcc($address, $name = '')
	{
		return $this->addanaddress('cc', $address, $name);
	}
	
	/**
	 * adds a "bcc" address.
	 * note: this function works with the smtp mailer on win32, not with the "mail" mailer.
	 * @param string $address
	 * @param string $name
	 * @return boolean true on success, false if address already used
	 */
	public function addbcc($address, $name = '')
	{
		return $this->addanaddress('bcc', $address, $name);
	}
	
	/**
	 * adds a "reply-to" address.
	 * @param string $address
	 * @param string $name
	 * @return boolean
	 */
	public function addreplyto($address, $name = '')
	{
		return $this->addanaddress('replyto', $address, $name);
	}
	
	/**
	 * adds an address to one of the recipient arrays
	 * addresses that have been added already return false, but do not throw exceptions
	 * @param string $kind one of 'to', 'cc', 'bcc', 'replyto'
	 * @param string $address the email address to send to
	 * @param string $name
	 * @return boolean true on success, false if address already used or invalid in some way
	 * @access private
	 */
	private function addanaddress($kind, $address, $name = '')
	{
		if (! preg_match('/^(to|cc|bcc|replyto)$/', $kind))
		{
			echo 'invalid recipient array: ' . kind;
			return false;
		}
		$address = trim($address);
		$name = trim(preg_replace('/[\r\n]+/', '', $name)); //strip breaks and trim
		if (! self::validateaddress($address))
		{
			$this->seterror($this->lang('invalid_address') . ': ' . $address);
			if ($this->exceptions)
			{
				throw new phpmailerexception($this->lang('invalid_address') . ': ' . $address);
			}
			echo $this->lang('invalid_address') . ': ' . $address;
			return false;
		}
		if ($kind != 'replyto')
		{
			if (! isset($this->all_recipients[strtolower($address)]))
			{
				array_push($this->$kind, array(
						$address, 
						$name));
				$this->all_recipients[strtolower($address)] = true;
				return true;
			}
		}
		else
		{
			if (! array_key_exists(strtolower($address), $this->replyto))
			{
				$this->replyto[strtolower($address)] = array(
						$address, 
						$name);
				return true;
			}
		}
		return false;
	}
	
	/**
	 * set the from and fromname properties
	 * @param string $address
	 * @param string $name
	 * @return boolean
	 */
	public function setfrom($address, $name = '', $auto = 1)
	{
		$address = trim($address);
		$name = trim(preg_replace('/[\r\n]+/', '', $name)); //strip breaks and trim
		if (! self::validateaddress($address))
		{
			$this->seterror($this->lang('invalid_address') . ': ' . $address);
			if ($this->exceptions)
			{
				throw new phpmailerexception($this->lang('invalid_address') . ': ' . $address);
			}
			echo $this->lang('invalid_address') . ': ' . $address;
			return false;
		}
		$this->from = $address;
		$this->fromname = $name;
		if ($auto)
		{
			if (empty($this->replyto))
			{
				$this->addanaddress('replyto', $address, $name);
			}
			if (empty($this->sender))
			{
				$this->sender = $address;
			}
		}
		return true;
	}
	
	/**
	 * check that a string looks roughly like an email address should
	 * static so it can be used without instantiation
	 * tries to use php built-in validator in the filter extension (from php 5.2), falls back to a reasonably competent regex validator
	 * conforms approximately to rfc2822
	 * @link http://www.hexillion.com/samples/#regex original pattern found here
	 * @param string $address the email address to check
	 * @return boolean
	 * @static
	 * @access public
	 */
	public static function validateaddress($address)
	{

			return preg_match('/^(?:[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+\.)*[\w\!\#\$\%\&\'\*\+\-\/\=\?\^\`\{\|\}\~]+@(?:(?:(?:[a-za-z0-9_](?:[a-za-z0-9_\-](?!\.)){0,61}[a-za-z0-9_-]?\.)+[a-za-z0-9_](?:[a-za-z0-9_\-](?!$)){0,61}[a-za-z0-9_]?)|(?:\[(?:(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\.){3}(?:[01]?\d{1,2}|2[0-4]\d|25[0-5])\]))$/', $address);

	}
	
	/////////////////////////////////////////////////
	// methods, mail sending
	/////////////////////////////////////////////////
	

	public function assign_vars($vars)
	{
		$this->vars = (empty($this->vars)) ? $vars : $this->vars . $vars;
	}
	
	/**
	 * myway
	 * @return bool
	 */
	public function sendmail($tpl_file)
	{
		
		$fd = @fopen($this->tpl_path . $tpl_file . '.tpl', 'r');
		
		$this->body = fread($fd, filesize($this->tpl_path . $tpl_file . '.tpl'));
		
		fclose($fd);
		
		// escape all quotes, else the eval will fail.
		$this->body = str_replace("'", "\'", $this->body);
		$this->body = preg_replace('#\{([a-zA-Z0-9\-_]*?)\}#is', "' . $\\1 . '", $this->body);
		
		reset($this->vars);
		while(list ( $key, $val ) = each($this->vars))
		{
			$$key = $val;
		}
		
		eval("\$this->body = '$this->body';");
		// clear vars
		reset($this->vars);
		while(list ( $key, $val ) = each($this->vars))
		{
			unset($$key);
		}
		
		preg_replace("#(?<!\r)\n#s", "\n", $this->body);
		
		$this->altbody = strip_tags($this->body);

		try
		{
			$this->send();
			$this->clearaddresses();
			$this->clearattachments();

		}
		catch ( phpmailerexception $e )
		{
			$this->seterror($e->getmessage());
			if ($this->exceptions)
			{
				throw $e;
			}
			echo $e->getmessage() . "\n";

			return false;
		}
		return true;

	}
	/**
	 * creates message and assigns mailer. if the message is
	 * not sent successfully then it returns false.  use the errorinfo
	 * variable to view description of the error.
	 * @return bool
	 */
	public function send()
	{
		try
		{
			if ((count($this->to) + count($this->cc) + count($this->bcc)) < 1)
			{
				throw new phpmailerexception($this->lang('provide_address'), self::stop_critical);
			}
			
			// set whether the message is multipart/alternative
			if (! empty($this->altbody))
			{
				$this->contenttype = 'multipart/alternative';
			}
			
			$this->error_count = 0; // reset errors
			$this->setmessagetype();
			$header = $this->createheader();
			$body = $this->createbody();
			
			if (empty($this->body))
			{
				throw new phpmailerexception($this->lang('empty_message'), self::stop_critical);
			}
			
			// digitally sign with dkim if enabled
			if ($this->dkim_domain && $this->dkim_private)
			{
				$header_dkim = $this->dkim_add($header, $this->subject, $body);
				$header = str_replace("\r\n", "\n", $header_dkim) . $header;
			}
			
			// choose the mailer and send through it
			switch ($this->mailer)
			{
				case 'sendmail' :
					$sendaction = $this->sendmailsend($header, $body);
					return $sendaction;
				default :
					$sendaction = $this->mailsend($header, $body);
					return $sendaction;
			}
		
		}
		catch ( phpmailerexception $e )
		{
			$this->seterror($e->getmessage());
			if ($this->exceptions)
			{
				throw $e;
			}
			echo $e->getmessage() . "\n";
			return false;
		}
	}
	
	/**
	 * sends mail using the $sendmail program.
	 * @param string $header the message headers
	 * @param string $body the message body
	 * @access protected
	 * @return bool
	 */
	protected function sendmailsend($header, $body)
	{
		if ($this->sender != '')
		{
			$sendmail = sprintf("%s -oi -f %s -t", escapeshellcmd($this->sendmail), escapeshellarg($this->sender));
		}
		else
		{
			$sendmail = sprintf("%s -oi -t", escapeshellcmd($this->sendmail));
		}
		if ($this->singleto === true)
		{
			foreach ( $this->singletoarray as $key => $val )
			{
				if (! @$mail = popen($sendmail, 'w'))
				{
					throw new phpmailerexception($this->lang('execute') . $this->sendmail, self::stop_critical);
				}
				fputs($mail, "to: " . $val . "\n");
				fputs($mail, $header);
				fputs($mail, $body);
				$result = pclose($mail);
				// implement call back function if it exists
				$issent = ($result == 0) ? 1 : 0;
				$this->docallback($issent, $val, $this->cc, $this->bcc, $this->subject, $body);
				if ($result != 0)
				{
					throw new phpmailerexception($this->lang('execute') . $this->sendmail, self::stop_critical);
				}
			}
		}
		else
		{
			if (! @$mail = popen($sendmail, 'w'))
			{
				throw new phpmailerexception($this->lang('execute') . $this->sendmail, self::stop_critical);
			}
			fputs($mail, $header);
			fputs($mail, $body);
			$result = pclose($mail);
			// implement call back function if it exists
			$issent = ($result == 0) ? 1 : 0;
			$this->docallback($issent, $this->to, $this->cc, $this->bcc, $this->subject, $body);
			if ($result != 0)
			{
				throw new phpmailerexception($this->lang('execute') . $this->sendmail, self::stop_critical);
			}
		}
		return true;
	}
	
	/**
	 * sends mail using the php mail() function.
	 * @param string $header the message headers
	 * @param string $body the message body
	 * @access protected
	 * @return bool
	 */
	protected function mailsend($header, $body)
	{
		$toarr = array();
		foreach ( $this->to as $t )
		{
			$toarr[] = $this->addrformat($t);
		}
		$to = implode(', ', $toarr);
		
		$params = sprintf("-oi -f %s", $this->sender);
		if ($this->sender != '' && strlen(ini_get('safe_mode')) < 1)
		{
			$old_from = ini_get('sendmail_from');
			ini_set('sendmail_from', $this->sender);
			if ($this->singleto === true && count($toarr) > 1)
			{
				foreach ( $toarr as $key => $val )
				{
					$rt = mail($val, $this->encodeheader($this->secureheader($this->subject)), $body, $header, $params);
					// implement call back function if it exists
					$issent = ($rt == 1) ? 1 : 0;
					$this->docallback($issent, $val, $this->cc, $this->bcc, $this->subject, $body);
				}
			}
			else
			{
				$rt = mail($to, $this->encodeheader($this->secureheader($this->subject)), $body, $header, $params);
				// implement call back function if it exists
				$issent = ($rt == 1) ? 1 : 0;
				$this->docallback($issent, $to, $this->cc, $this->bcc, $this->subject, $body);
			}
		}
		else
		{
			if ($this->singleto === true && count($toarr) > 1)
			{
				foreach ( $toarr as $key => $val )
				{
					$rt = mail($val, $this->encodeheader($this->secureheader($this->subject)), $body, $header, $params);
					// implement call back function if it exists
					$issent = ($rt == 1) ? 1 : 0;
					$this->docallback($issent, $val, $this->cc, $this->bcc, $this->subject, $body);
				}
			}
			else
			{
				$rt = mail($to, $this->encodeheader($this->secureheader($this->subject)), $body, $header);
				// implement call back function if it exists
				$issent = ($rt == 1) ? 1 : 0;
				$this->docallback($issent, $to, $this->cc, $this->bcc, $this->subject, $body);
			}
		}
		if (isset($old_from))
		{
			ini_set('sendmail_from', $old_from);
		}
		if (! $rt)
		{
			//throw new phpmailerexception($this->lang('instantiate'), self::stop_critical);
		}
		return true;
	}
	
	/**
	 * sets the language for all class error messages.
	 * returns false if it cannot load the language file.  the default language is english.
	 * @param string $langcode iso 639-1 2-character language code (e.g. portuguese: "br")
	 * @param string $lang_path path to the language file directory
	 * @access public
	 */
	function setlanguage($langcode = 'en', $lang_path = 'language/')
	{
		//define full set of translatable strings
		$phpmailer_lang = array(
				'provide_address' => 'you must provide at least one recipient email address.', 
				'mailer_not_supported' => ' mailer is not supported.', 
				'execute' => 'could not execute: ', 
				'instantiate' => 'could not instantiate mail function.', 
				'from_failed' => 'the following from address failed: ', 
				'file_access' => 'could not access file: ', 
				'file_open' => 'file error: could not open file: ', 
				'encoding' => 'unknown encoding: ', 
				'signing' => 'signing error: ', 
				'empty_message' => 'message body empty', 
				'invalid_address' => 'invalid address', 
				'variable_set' => 'cannot set or reset variable: ');
		//overwrite language-specific strings. this way we'll never have missing translations - no more "language string failed to load"!
		$l = true;
		if ($langcode != 'en')
		{ //there is no english translation file
			$l = @include $lang_path . 'phpmailer.lang-' . $langcode . '.php';
		}
		$this->language = $phpmailer_lang;
		return ($l == true); //returns false if language not found
	}
	
	/**
	 * return the current array of language strings
	 * @return array
	 */
	public function gettranslations()
	{
		return $this->language;
	}
	
	/////////////////////////////////////////////////
	// methods, message creation
	/////////////////////////////////////////////////
	

	/**
	 * creates recipient headers.
	 * @access public
	 * @return string
	 */
	public function addrappend($type, $addr)
	{
		$addr_str = $type . ': ';
		$addresses = array();
		foreach ( $addr as $a )
		{
			$addresses[] = $this->addrformat($a);
		}
		$addr_str .= implode(', ', $addresses);
		$addr_str .= $this->le;
		
		return $addr_str;
	}
	
	/**
	 * formats an address correctly.
	 * @access public
	 * @return string
	 */
	public function addrformat($addr)
	{
		if (empty($addr[1]))
		{
			return $this->secureheader($addr[0]);
		}
		else
		{
			return $this->encodeheader($this->secureheader($addr[1]), 'phrase') . " <" . $this->secureheader($addr[0]) . ">";
		}
	}
	
	/**
	 * wraps message for use with mailers that do not
	 * automatically perform wrapping and for quoted-printable.
	 * original written by philippe.
	 * @param string $message the message to wrap
	 * @param integer $length the line length to wrap to
	 * @param boolean $qp_mode whether to run in quoted-printable mode
	 * @access public
	 * @return string
	 */
	public function wraptext($message, $length, $qp_mode = false)
	{
		$soft_break = ($qp_mode) ? sprintf(" =%s", $this->le) : $this->le;
		// if utf-8 encoding is used, we will need to make sure we don't
		// split multibyte characters when we wrap
		$is_utf8 = (strtolower($this->charset) == "utf-8");
		
		$message = $this->fixeol($message);
		if (substr($message, - 1) == $this->le)
		{
			$message = substr($message, 0, - 1);
		}
		
		$line = explode($this->le, $message);
		$message = '';
		for($i = 0; $i < count($line); $i++)
		{
			$line_part = explode(' ', $line[$i]);
			$buf = '';
			for($e = 0; $e < count($line_part); $e++)
			{
				$word = $line_part[$e];
				if ($qp_mode and (strlen($word) > $length))
				{
					$space_left = $length - strlen($buf) - 1;
					if ($e != 0)
					{
						if ($space_left > 20)
						{
							$len = $space_left;
							if ($is_utf8)
							{
								$len = $this->utf8charboundary($word, $len);
							}
							elseif (substr($word, $len - 1, 1) == "=")
							{
								$len--;
							}
							elseif (substr($word, $len - 2, 1) == "=")
							{
								$len -= 2;
							}
							$part = substr($word, 0, $len);
							$word = substr($word, $len);
							$buf .= ' ' . $part;
							$message .= $buf . sprintf("=%s", $this->le);
						}
						else
						{
							$message .= $buf . $soft_break;
						}
						$buf = '';
					}
					while(strlen($word) > 0)
					{
						$len = $length;
						if ($is_utf8)
						{
							$len = $this->utf8charboundary($word, $len);
						}
						elseif (substr($word, $len - 1, 1) == "=")
						{
							$len--;
						}
						elseif (substr($word, $len - 2, 1) == "=")
						{
							$len -= 2;
						}
						$part = substr($word, 0, $len);
						$word = substr($word, $len);
						
						if (strlen($word) > 0)
						{
							$message .= $part . sprintf("=%s", $this->le);
						}
						else
						{
							$buf = $part;
						}
					}
				}
				else
				{
					$buf_o = $buf;
					$buf .= ($e == 0) ? $word : (' ' . $word);
					
					if (strlen($buf) > $length and $buf_o != '')
					{
						$message .= $buf_o . $soft_break;
						$buf = $word;
					}
				}
			}
			$message .= $buf . $this->le;
		}
		
		return $message;
	}
	
	/**
	 * finds last character boundary prior to maxlength in a utf-8
	 * quoted (printable) encoded string.
	 * original written by colin brown.
	 * @access public
	 * @param string $encodedtext utf-8 qp text
	 * @param int    $maxlength   find last character boundary prior to this length
	 * @return int
	 */
	public function utf8charboundary($encodedtext, $maxlength)
	{
		$foundsplitpos = false;
		$lookback = 3;
		while(! $foundsplitpos)
		{
			$lastchunk = substr($encodedtext, $maxlength - $lookback, $lookback);
			$encodedcharpos = strpos($lastchunk, "=");
			if ($encodedcharpos !== false)
			{
				// found start of encoded character byte within $lookback block.
				// check the encoded byte value (the 2 chars after the '=')
				$hex = substr($encodedtext, $maxlength - $lookback + $encodedcharpos + 1, 2);
				$dec = hexdec($hex);
				if ($dec < 128)
				{ // single byte character.
					// if the encoded char was found at pos 0, it will fit
					// otherwise reduce maxlength to start of the encoded char
					$maxlength = ($encodedcharpos == 0) ? $maxlength : $maxlength - ($lookback - $encodedcharpos);
					$foundsplitpos = true;
				}
				elseif ($dec >= 192)
				{ // first byte of a multi byte character
					// reduce maxlength to split at start of character
					$maxlength = $maxlength - ($lookback - $encodedcharpos);
					$foundsplitpos = true;
				}
				elseif ($dec < 192)
				{ // middle byte of a multi byte character, look further back
					$lookback += 3;
				}
			}
			else
			{
				// no encoded character found
				$foundsplitpos = true;
			}
		}
		return $maxlength;
	}
	
	/**
	 * set the body wrapping.
	 * @access public
	 * @return void
	 */
	public function setwordwrap()
	{
		if ($this->wordwrap < 1)
		{
			return;
		}
		switch ($this->message_type)
		{
			case 'alt' :
			case 'alt_attachments' :
				$this->altbody = $this->wraptext($this->altbody, $this->wordwrap);
			break;
			default :
				$this->body = $this->wraptext($this->body, $this->wordwrap);
			break;
		}
	}
	
	/**
	 * assembles message header.
	 * @access public
	 * @return string the assembled header
	 */
	public function createheader()
	{
		$result = '';
		
		// set the boundaries
		$uniq_id = md5(uniqid(time()));
		$this->boundary[1] = 'b1_' . $uniq_id;
		$this->boundary[2] = 'b2_' . $uniq_id;
		
		$result .= $this->headerline('date', self::rfcdate());
		if ($this->sender == '')
		{
			$result .= $this->headerline('return-path', trim($this->from));
		}
		else
		{
			$result .= $this->headerline('return-path', trim($this->sender));
		}
		
		// to be created automatically by mail()
		if ($this->mailer != 'mail')
		{
			if ($this->singleto === true)
			{
				foreach ( $this->to as $t )
				{
					$this->singletoarray[] = $this->addrformat($t);
				}
			}
			else
			{
				if (count($this->to) > 0)
				{
					$result .= $this->addrappend('to', $this->to);
				}
				elseif (count($this->cc) == 0)
				{
					$result .= $this->headerline('to', 'undisclosed-recipients:;');
				}
			}
		}
		
		$from = array();
		$from[0][0] = trim($this->from);
		$from[0][1] = $this->fromname;
		$result .= $this->addrappend('from', $from);
		
		// sendmail and mail() extract cc from the header before sending
		if (count($this->cc) > 0)
		{
			$result .= $this->addrappend('cc', $this->cc);
		}
		
		// sendmail and mail() extract bcc from the header before sending
		if (count($this->bcc) > 0)
		{
			$result .= $this->addrappend('bcc', $this->bcc);
		}
		
		if (count($this->replyto) > 0)
		{
			$result .= $this->addrappend('reply-to', $this->replyto);
		}
		
		// mail() sets the subject itself
		if ($this->mailer != 'mail')
		{
			$result .= $this->headerline('subject', $this->encodeheader($this->secureheader($this->subject)));
		}
		
		if ($this->messageid != '')
		{
			$result .= $this->headerline('message-id', $this->messageid);
		}
		else
		{
			$result .= sprintf("message-id: <%s@%s>%s", $uniq_id, $this->serverhostname(), $this->le);
		}
		$result .= $this->headerline('x-priority', $this->priority);
		/*
    $result .= $this->headerline('x-mailer', 'phpmailer '.$this->version.' (phpmailer.codeworxtech.com)');
    */
		if ($this->confirmreadingto != '')
		{
			$result .= $this->headerline('disposition-notification-to', '<' . trim($this->confirmreadingto) . '>');
		}
		
		// add custom headers
		for($index = 0; $index < count($this->customheader); $index++)
		{
			$result .= $this->headerline(trim($this->customheader[$index][0]), $this->encodeheader(trim($this->customheader[$index][1])));
		}
		if (! $this->sign_key_file)
		{
			$result .= $this->headerline('mime-version', '1.0');
			$result .= $this->getmailmime();
		}
		
		return $result;
	}
	
	/**
	 * returns the message mime.
	 * @access public
	 * @return string
	 */
	public function getmailmime()
	{
		$result = '';
		switch ($this->message_type)
		{
			case 'plain' :
				$result .= $this->headerline('content-transfer-encoding', $this->encoding);
				$result .= sprintf("content-type: %s; charset=\"%s\"", $this->contenttype, $this->charset);
			break;
			case 'attachments' :
			case 'alt_attachments' :
				if ($this->inlineimageexists())
				{
					$result .= sprintf("content-type: %s;%s\ttype=\"text/html\";%s\tboundary=\"%s\"%s", 'multipart/related', $this->le, $this->le, $this->boundary[1], $this->le);
				}
				else
				{
					$result .= $this->headerline('content-type', 'multipart/mixed;');
					$result .= $this->textline("\tboundary=\"" . $this->boundary[1] . '"');
				}
			break;
			case 'alt' :
				$result .= $this->headerline('content-type', 'multipart/alternative;');
				$result .= $this->textline("\tboundary=\"" . $this->boundary[1] . '"');
			break;
		}
		
		if ($this->mailer != 'mail')
		{
			$result .= $this->le . $this->le;
		}
		
		return $result;
	}
	
	/**
	 * assembles the message body.  returns an empty string on failure.
	 * @access public
	 * @return string the assembled message body
	 */
	public function createbody()
	{
		$body = '';
		
		if ($this->sign_key_file)
		{
			$body .= $this->getmailmime();
		}
		
		$this->setwordwrap();
		
		switch ($this->message_type)
		{
			case 'alt' :
				$body .= $this->getboundary($this->boundary[1], '', 'text/plain', '');
				$body .= $this->encodestring($this->altbody, $this->encoding);
				$body .= $this->le . $this->le;
				$body .= $this->getboundary($this->boundary[1], '', 'text/html', '');
				$body .= $this->encodestring($this->body, $this->encoding);
				$body .= $this->le . $this->le;
				$body .= $this->endboundary($this->boundary[1]);
			break;
			case 'plain' :
				$body .= $this->encodestring($this->body, $this->encoding);
			break;
			case 'attachments' :
				$body .= $this->getboundary($this->boundary[1], '', '', '');
				$body .= $this->encodestring($this->body, $this->encoding);
				$body .= $this->le;
				$body .= $this->attachall();
			break;
			case 'alt_attachments' :
				$body .= sprintf("--%s%s", $this->boundary[1], $this->le);
				$body .= sprintf("content-type: %s;%s" . "\tboundary=\"%s\"%s", 'multipart/alternative', $this->le, $this->boundary[2], $this->le . $this->le);
				$body .= $this->getboundary($this->boundary[2], '', 'text/plain', '') . $this->le; // create text body
				$body .= $this->encodestring($this->altbody, $this->encoding);
				$body .= $this->le . $this->le;
				$body .= $this->getboundary($this->boundary[2], '', 'text/html', '') . $this->le; // create the html body
				$body .= $this->encodestring($this->body, $this->encoding);
				$body .= $this->le . $this->le;
				$body .= $this->endboundary($this->boundary[2]);
				$body .= $this->attachall();
			break;
		}
		
		if ($this->iserror())
		{
			$body = '';
		}
		elseif ($this->sign_key_file)
		{
			try
			{
				$file = tempnam('', 'mail');
				file_put_contents($file, $body); //todo check this worked
				$signed = tempnam("", "signed");
				if (@openssl_pkcs7_sign($file, $signed, "file://" . $this->sign_cert_file, array(
						"file://" . $this->sign_key_file, 
						$this->sign_key_pass), null))
				{
					@unlink($file);
					@unlink($signed);
					$body = file_get_contents($signed);
				}
				else
				{
					@unlink($file);
					@unlink($signed);
					throw new phpmailerexception($this->lang("signing") . openssl_error_string());
				}
			}
			catch ( phpmailerexception $e )
			{
				$body = '';
				if ($this->exceptions)
				{
					throw $e;
				}
			}
		}
		
		return $body;
	}
	
	/**
	 * returns the start of a message boundary.
	 * @access private
	 */
	private function getboundary($boundary, $charset, $contenttype, $encoding)
	{
		$result = '';
		if ($charset == '')
		{
			$charset = $this->charset;
		}
		if ($contenttype == '')
		{
			$contenttype = $this->contenttype;
		}
		if ($encoding == '')
		{
			$encoding = $this->encoding;
		}
		$result .= $this->textline('--' . $boundary);
		$result .= sprintf("content-type: %s; charset = \"%s\"", $contenttype, $charset);
		$result .= $this->le;
		$result .= $this->headerline('content-transfer-encoding', $encoding);
		$result .= $this->le;
		
		return $result;
	}
	
	/**
	 * returns the end of a message boundary.
	 * @access private
	 */
	private function endboundary($boundary)
	{
		return $this->le . '--' . $boundary . '--' . $this->le;
	}
	
	/**
	 * sets the message type.
	 * @access private
	 * @return void
	 */
	private function setmessagetype()
	{
		if (count($this->attachment) < 1 && strlen($this->altbody) < 1)
		{
			$this->message_type = 'plain';
		}
		else
		{
			if (count($this->attachment) > 0)
			{
				$this->message_type = 'attachments';
			}
			if (strlen($this->altbody) > 0 && count($this->attachment) < 1)
			{
				$this->message_type = 'alt';
			}
			if (strlen($this->altbody) > 0 && count($this->attachment) > 0)
			{
				$this->message_type = 'alt_attachments';
			}
		}
	}
	
	/**
	 * returns a formatted header line.
	 * @access public
	 * @return string
	 */
	public function headerline($name, $value)
	{
		return $name . ': ' . $value . $this->le;
	}
	
	/**
	 * returns a formatted mail line.
	 * @access public
	 * @return string
	 */
	public function textline($value)
	{
		return $value . $this->le;
	}
	
	/////////////////////////////////////////////////
	// class methods, attachments
	/////////////////////////////////////////////////
	

	/**
	 * adds an attachment from a path on the filesystem.
	 * returns false if the file could not be found
	 * or accessed.
	 * @param string $path path to the attachment.
	 * @param string $name overrides the attachment name.
	 * @param string $encoding file encoding (see $encoding).
	 * @param string $type file extension (mime) type.
	 * @return bool
	 */
	public function addattachment($path, $name = '', $encoding = 'base64', $type = 'application/octet-stream')
	{
		try
		{
			if (! @is_file($path))
			{
				throw new phpmailerexception($this->lang('file_access') . $path, self::stop_continue);
			}
			$filename = basename($path);
			if ($name == '')
			{
				$name = $filename;
			}
			
			$this->attachment[] = array(
					0 => $path, 
					1 => $filename, 
					2 => $name, 
					3 => $encoding, 
					4 => $type, 
					5 => false,  // isstringattachment
					6 => 'attachment', 
					7 => 0);
		
		}
		catch ( phpmailerexception $e )
		{
			$this->seterror($e->getmessage());
			if ($this->exceptions)
			{
				throw $e;
			}
			echo $e->getmessage() . "\n";
			if ($e->getcode() == self::stop_critical)
			{
				return false;
			}
		}
		return true;
	}
	
	/**
	 * return the current array of attachments
	 * @return array
	 */
	public function getattachments()
	{
		return $this->attachment;
	}
	
	/**
	 * attaches all fs, string, and binary attachments to the message.
	 * returns an empty string on failure.
	 * @access private
	 * @return string
	 */
	private function attachall()
	{
		// return text of body
		$mime = array();
		$ciduniq = array();
		$incl = array();
		
		// add all attachments
		foreach ( $this->attachment as $attachment )
		{
			// check for string attachment
			$bstring = $attachment[5];
			if ($bstring)
			{
				$string = $attachment[0];
			}
			else
			{
				$path = $attachment[0];
			}
			
			if (in_array($attachment[0], $incl))
			{
				continue;
			}
			$filename = $attachment[1];
			$name = $attachment[2];
			$encoding = $attachment[3];
			$type = $attachment[4];
			$disposition = $attachment[6];
			$cid = $attachment[7];
			$incl[] = $attachment[0];
			if ($disposition == 'inline' && isset($ciduniq[$cid]))
			{
				continue;
			}
			$ciduniq[$cid] = true;
			
			$mime[] = sprintf("--%s%s", $this->boundary[1], $this->le);
			$mime[] = sprintf("content-type: %s; name=\"%s\"%s", $type, $this->encodeheader($this->secureheader($name)), $this->le);
			$mime[] = sprintf("content-transfer-encoding: %s%s", $encoding, $this->le);
			
			if ($disposition == 'inline')
			{
				$mime[] = sprintf("content-id: <%s>%s", $cid, $this->le);
			}
			
			$mime[] = sprintf("content-disposition: %s; filename=\"%s\"%s", $disposition, $this->encodeheader($this->secureheader($name)), $this->le . $this->le);
			
			// encode as string attachment
			if ($bstring)
			{
				$mime[] = $this->encodestring($string, $encoding);
				if ($this->iserror())
				{
					return '';
				}
				$mime[] = $this->le . $this->le;
			}
			else
			{
				$mime[] = $this->encodefile($path, $encoding);
				if ($this->iserror())
				{
					return '';
				}
				$mime[] = $this->le . $this->le;
			}
		}
		
		$mime[] = sprintf("--%s--%s", $this->boundary[1], $this->le);
		
		return join('', $mime);
	}
	
	/**
	 * encodes attachment in requested format.
	 * returns an empty string on failure.
	 * @param string $path the full path to the file
	 * @param string $encoding the encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
	 * @see encodefile()
	 * @access private
	 * @return string
	 */
	private function encodefile($path, $encoding = 'base64')
	{
		try
		{
			if (! is_readable($path))
			{
				throw new phpmailerexception($this->lang('file_open') . $path, self::stop_continue);
			}
			if (function_exists('get_magic_quotes'))
			{
				function get_magic_quotes()
				{
					return false;
				}
			}
			if (php_version < 6)
			{
				$magic_quotes = get_magic_quotes_runtime();
				set_magic_quotes_runtime(0);
			}
			$file_buffer = file_get_contents($path);
			$file_buffer = $this->encodestring($file_buffer, $encoding);
			if (php_version < 6)
			{
				set_magic_quotes_runtime($magic_quotes);
			}
			return $file_buffer;
		}
		catch ( exception $e )
		{
			$this->seterror($e->getmessage());
			return '';
		}
	}
	
	/**
	 * encodes string to requested format.
	 * returns an empty string on failure.
	 * @param string $str the text to encode
	 * @param string $encoding the encoding to use; one of 'base64', '7bit', '8bit', 'binary', 'quoted-printable'
	 * @access public
	 * @return string
	 */
	public function encodestring($str, $encoding = 'base64')
	{
		$encoded = '';
		switch (strtolower($encoding))
		{
			case 'base64' :
				$encoded = chunk_split(base64_encode($str), 76, $this->le);
			break;
			case '7bit' :
			case '8bit' :
				$encoded = $this->fixeol($str);
				//make sure it ends with a line break
				if (substr($encoded, - (strlen($this->le))) != $this->le)
					$encoded .= $this->le;
			break;
			case 'binary' :
				$encoded = $str;
			break;
			case 'quoted-printable' :
				$encoded = $this->encodeqp($str);
			break;
			default :
				$this->seterror($this->lang('encoding') . $encoding);
			break;
		}
		return $encoded;
	}
	
	/**
	 * encode a header string to best (shortest) of q, b, quoted or none.
	 * @access public
	 * @return string
	 */
	public function encodeheader($str, $position = 'text')
	{
		

		$x = 0;
		
		switch (strtolower($position))
		{
			case 'phrase' :
				if (! preg_match('/[\200-\377]/', $str))
				{
					// can't use addslashes as we don't know what value has magic_quotes_sybase
					$encoded = addcslashes($str, "\0..\37\177\\\"");
					if (($str == $encoded) && ! preg_match('/[^a-za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str))
					{
						return ($encoded);
					}
					else
					{
						return ("\"$encoded\"");
					}
				}
				$x = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
			break;
			case 'comment' :
				$x = preg_match_all('/[()"]/', $str, $matches);
			// fall-through
			case 'text' :
			default :
				$x += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
			break;
		}

		if ($x == 0)
		{
			return ($str);
		}
		
		$maxlen = 75 - 7 - strlen($this->charset);
		// try to select the encoding which should produce the shortest output
		if (strlen($str) / 3 < $x)
		{
			$encoding = 'b';
			if (function_exists('mb_strlen') && $this->hasmultibytes($str))
			{
				// use a custom function which correctly encodes and wraps long
				// multibyte strings without breaking lines within a character
				$encoded = $this->base64encodewrapmb($str);
			}
			else
			{
				$encoded = base64_encode($str);
				$maxlen -= $maxlen % 4;
				$encoded = trim(chunk_split($encoded, $maxlen, "\n"));
			}
		}
		else
		{
			$encoding = 'q';
			$encoded = $this->encodeq($str, $position);
			$encoded = $this->wraptext($encoded, $maxlen, true);
			$encoded = str_replace('=' . $this->le, "\n", trim($encoded));
		}
		
		$encoded = preg_replace('/^(.*)$/m', " =?" . $this->charset . "?$encoding?\\1?=", $encoded);
		$encoded = trim(str_replace("\n", $this->le, $encoded));
		
		return $encoded;
	}
	
	/**
	 * checks if a string contains multibyte characters.
	 * @access public
	 * @param string $str multi-byte text to wrap encode
	 * @return bool
	 */
	public function hasmultibytes($str)
	{
		if (function_exists('mb_strlen'))
		{
			return (strlen($str) > mb_strlen($str, $this->charset));
		}
		else
		{ // assume no multibytes (we can't handle without mbstring functions anyway)
			return false;
		}
	}
	
	/**
	 * correctly encodes and wraps long multibyte strings for mail headers
	 * without breaking lines within a character.
	 * adapted from a function by paravoid at http://uk.php.net/manual/en/function.mb-encode-mimeheader.php
	 * @access public
	 * @param string $str multi-byte text to wrap encode
	 * @return string
	 */
	public function base64encodewrapmb($str)
	{
		$start = "=?" . $this->charset . "?b?";
		$end = "?=";
		$encoded = "";
		
		$mb_length = mb_strlen($str, $this->charset);
		// each line must have length <= 75, including $start and $end
		$length = 75 - strlen($start) - strlen($end);
		// average multi-byte ratio
		$ratio = $mb_length / strlen($str);
		// base64 has a 4:3 ratio
		$offset = $avglength = floor($length * $ratio * .75);
		
		for($i = 0; $i < $mb_length; $i += $offset)
		{
			$lookback = 0;
			
			do
			{
				$offset = $avglength - $lookback;
				$chunk = mb_substr($str, $i, $offset, $this->charset);
				$chunk = base64_encode($chunk);
				$lookback++;
			} while(strlen($chunk) > $length);
			
			$encoded .= $chunk . $this->le;
		}
		
		// chomp the last linefeed
		$encoded = substr($encoded, 0, - strlen($this->le));
		return $encoded;
	}
	
	/**
	 * encode string to quoted-printable.
	 * only uses standard php, slow, but will always work
	 * @access public
	 * @param string $string the text to encode
	 * @param integer $line_max number of chars allowed on a line before wrapping
	 * @return string
	 */
	public function encodeqpphp($input = '', $line_max = 76, $space_conv = false)
	{
		$hex = array(
				'0', 
				'1', 
				'2', 
				'3', 
				'4', 
				'5', 
				'6', 
				'7', 
				'8', 
				'9', 
				'a', 
				'b', 
				'c', 
				'd', 
				'e', 
				'f');
		$lines = preg_split('/(?:\r\n|\r|\n)/', $input);
		$eol = "\r\n";
		$escape = '=';
		$output = '';
		while(list ( , $line ) = each($lines))
		{
			$linlen = strlen($line);
			$newline = '';
			for($i = 0; $i < $linlen; $i++)
			{
				$c = substr($line, $i, 1);
				$dec = ord($c);
				if (($i == 0) && ($dec == 46))
				{ // convert first point in the line into =2e
					$c = '=2e';
				}
				if ($dec == 32)
				{
					if ($i == ($linlen - 1))
					{ // convert space at eol only
						$c = '=20';
					}
					else if ($space_conv)
					{
						$c = '=20';
					}
				}
				elseif (($dec == 61) || ($dec < 32) || ($dec > 126))
				{ // always encode "\t", which is *not* required
					$h2 = floor($dec / 16);
					$h1 = floor($dec % 16);
					$c = $escape . $hex[$h2] . $hex[$h1];
				}
				if ((strlen($newline) + strlen($c)) >= $line_max)
				{ // crlf is not counted
					$output .= $newline . $escape . $eol; //  soft line break; " =\r\n" is okay
					$newline = '';
					// check if newline first character will be point or not
					if ($dec == 46)
					{
						$c = '=2e';
					}
				}
				$newline .= $c;
			} // end of for
			$output .= $newline . $eol;
		} // end of while
		return $output;
	}
	
	/**
	 * encode string to rfc2045 (6.7) quoted-printable format
	 * uses a php5 stream filter to do the encoding about 64x faster than the old version
	 * also results in same content as you started with after decoding
	 * @see encodeqpphp()
	 * @access public
	 * @param string $string the text to encode
	 * @param integer $line_max number of chars allowed on a line before wrapping
	 * @param boolean $space_conv dummy param for compatibility with existing encodeqp function
	 * @return string
	 * @author marcus bointon
	 */
	public function encodeqp($string, $line_max = 76, $space_conv = false)
	{
		if (function_exists('quoted_printable_encode'))
		{ //use native function if it's available (>= php5.3)
			return quoted_printable_encode($string);
		}
		$filters = stream_get_filters();
		if (! in_array('convert.*', $filters))
		{ //got convert stream filter?
			return $this->encodeqpphp($string, $line_max, $space_conv); //fall back to old implementation
		}
		$fp = fopen('php://temp/', 'r+');
		$string = preg_replace('/\r\n?/', $this->le, $string); //normalise line breaks
		$params = array(
				'line-length' => $line_max, 
				'line-break-chars' => $this->le);
		$s = stream_filter_append($fp, 'convert.quoted-printable-encode', stream_filter_read, $params);
		fputs($fp, $string);
		rewind($fp);
		$out = stream_get_contents($fp);
		stream_filter_remove($s);
		$out = preg_replace('/^\./m', '=2e', $out); //encode . if it is first char on a line, workaround for bug in exchange
		fclose($fp);
		return $out;
	}
	
	/**
	 * encode string to q encoding.
	 * @link http://tools.ietf.org/html/rfc2047
	 * @param string $str the text to encode
	 * @param string $position where the text is going to be used, see the rfc for what that means
	 * @access public
	 * @return string
	 */
	public function encodeq($str, $position = 'text')
	{
		// there should not be any eol in the string
		$encoded = preg_replace('/[\r\n]*/', '', $str);
		
		switch (strtolower($position))
		{
			case 'phrase' :
				$encoded = preg_replace("/([^a-za-z0-9!*+\/ -])/e", "'='.sprintf('%02x', ord('\\1'))", $encoded);
			break;
			case 'comment' :
				$encoded = preg_replace("/([\(\)\"])/e", "'='.sprintf('%02x', ord('\\1'))", $encoded);
			case 'text' :
			default :
				// replace every high ascii, control =, ? and _ characters
				//todo using /e (equivalent to eval()) is probably not a good idea
				$encoded = preg_replace('/([\000-\011\013\014\016-\037\075\077\137\177-\377])/e', "'='.sprintf('%02x', ord('\\1'))", $encoded);
			break;
		}
		
		// replace every spaces to _ (more readable than =20)
		$encoded = str_replace(' ', '_', $encoded);
		
		return $encoded;
	}
	
	/**
	 * adds a string or binary attachment (non-filesystem) to the list.
	 * this method can be used to attach ascii or binary data,
	 * such as a blob record from a database.
	 * @param string $string string attachment data.
	 * @param string $filename name of the attachment.
	 * @param string $encoding file encoding (see $encoding).
	 * @param string $type file extension (mime) type.
	 * @return void
	 */
	public function addstringattachment($string, $filename, $encoding = 'base64', $type = 'application/octet-stream')
	{
		// append to $attachment array
		$this->attachment[] = array(
				0 => $string, 
				1 => $filename, 
				2 => basename($filename), 
				3 => $encoding, 
				4 => $type, 
				5 => true,  // isstringattachment
				6 => 'attachment', 
				7 => 0);
	}
	
	/**
	 * adds an embedded attachment.  this can include images, sounds, and
	 * just about any other document.  make sure to set the $type to an
	 * image type.  for jpeg images use "image/jpeg" and for gif images
	 * use "image/gif".
	 * @param string $path path to the attachment.
	 * @param string $cid content id of the attachment.  use this to identify
	 * the id for accessing the image in an html form.
	 * @param string $name overrides the attachment name.
	 * @param string $encoding file encoding (see $encoding).
	 * @param string $type file extension (mime) type.
	 * @return bool
	 */
	public function addembeddedimage($path, $cid, $name = '', $encoding = 'base64', $type = 'application/octet-stream')
	{
		
		if (! @is_file($path))
		{
			$this->seterror($this->lang('file_access') . $path);
			return false;
		}
		
		$filename = basename($path);
		if ($name == '')
		{
			$name = $filename;
		}
		
		// append to $attachment array
		$this->attachment[] = array(
				0 => $path, 
				1 => $filename, 
				2 => $name, 
				3 => $encoding, 
				4 => $type, 
				5 => false,  // isstringattachment
				6 => 'inline', 
				7 => $cid);
		
		return true;
	}
	
	/**
	 * returns true if an inline attachment is present.
	 * @access public
	 * @return bool
	 */
	public function inlineimageexists()
	{
		foreach ( $this->attachment as $attachment )
		{
			if ($attachment[6] == 'inline')
			{
				return true;
			}
		}
		return false;
	}
	
	/////////////////////////////////////////////////
	// class methods, message reset
	/////////////////////////////////////////////////
	

	/**
	 * clears all recipients assigned in the to array.  returns void.
	 * @return void
	 */
	public function clearaddresses()
	{
		foreach ( $this->to as $to )
		{
			unset($this->all_recipients[strtolower($to[0])]);
		}
		$this->to = array();
	}
	
	/**
	 * clears all recipients assigned in the cc array.  returns void.
	 * @return void
	 */
	public function clearccs()
	{
		foreach ( $this->cc as $cc )
		{
			unset($this->all_recipients[strtolower($cc[0])]);
		}
		$this->cc = array();
	}
	
	/**
	 * clears all recipients assigned in the bcc array.  returns void.
	 * @return void
	 */
	public function clearbccs()
	{
		foreach ( $this->bcc as $bcc )
		{
			unset($this->all_recipients[strtolower($bcc[0])]);
		}
		$this->bcc = array();
	}
	
	/**
	 * clears all recipients assigned in the replyto array.  returns void.
	 * @return void
	 */
	public function clearreplytos()
	{
		$this->replyto = array();
	}
	
	/**
	 * clears all recipients assigned in the to, cc and bcc
	 * array.  returns void.
	 * @return void
	 */
	public function clearallrecipients()
	{
		$this->to = array();
		$this->cc = array();
		$this->bcc = array();
		$this->all_recipients = array();
	}
	
	/**
	 * clears all previously set filesystem, string, and binary
	 * attachments.  returns void.
	 * @return void
	 */
	public function clearattachments()
	{
		$this->attachment = array();
	}
	
	/**
	 * clears all custom headers.  returns void.
	 * @return void
	 */
	public function clearcustomheaders()
	{
		$this->customheader = array();
	}
	
	/////////////////////////////////////////////////
	// class methods, miscellaneous
	/////////////////////////////////////////////////
	

	/**
	 * adds the error message to the error container.
	 * @access protected
	 * @return void
	 */
	protected function seterror($msg)
	{
		$this->error_count++;
		$this->errorinfo = $msg;
	}
	
	/**
	 * returns the proper rfc 822 formatted date.
	 * @access public
	 * @return string
	 * @static
	 */
	public static function rfcdate()
	{
		$tz = date('z');
		$tzs = ($tz < 0) ? '-' : '+';
		$tz = abs($tz);
		$tz = (int) ($tz / 3600) * 100 + ($tz % 3600) / 60;
		$result = sprintf("%s %s%04d", date('d, j m y h:i:s'), $tzs, $tz);
		
		return $result;
	}
	
	/**
	 * returns the server hostname or 'localhost.localdomain' if unknown.
	 * @access private
	 * @return string
	 */
	private function serverhostname()
	{
		if (! empty($this->hostname))
		{
			$result = $this->hostname;
		}
		elseif (isset($_server['server_name']))
		{
			$result = $_server['server_name'];
		}
		else
		{
			$result = 'localhost.localdomain';
		}
		
		return $result;
	}
	
	/**
	 * returns a message in the appropriate language.
	 * @access private
	 * @return string
	 */
	private function lang($key)
	{
		if (count($this->language) < 1)
		{
			$this->setlanguage('en'); // set the default language
		}
		
		if (isset($this->language[$key]))
		{
			return $this->language[$key];
		}
		else
		{
			return 'language string failed to load: ' . $key;
		}
	}
	
	/**
	 * returns true if an error occurred.
	 * @access public
	 * @return bool
	 */
	public function iserror()
	{
		return ($this->error_count > 0);
	}
	
	/**
	 * changes every end of line from cr or lf to crlf.
	 * @access private
	 * @return string
	 */
	private function fixeol($str)
	{
		$str = str_replace("\r\n", "\n", $str);
		$str = str_replace("\r", "\n", $str);
		$str = str_replace("\n", $this->le, $str);
		return $str;
	}
	
	/**
	 * adds a custom header.
	 * @access public
	 * @return void
	 */
	public function addcustomheader($custom_header)
	{
		$this->customheader[] = explode(':', $custom_header, 2);
	}
	
	/**
	 * evaluates the message and returns modifications for inline images and backgrounds
	 * @access public
	 * @return $message
	 */
	public function msghtml($message, $basedir = '')
	{
		preg_match_all("/(src|background)=\"(.*)\"/ui", $message, $images);
		if (isset($images[2]))
		{
			foreach ( $images[2] as $i => $url )
			{
				// do not change urls for absolute images (thanks to corvuscorax)
				if (! preg_match('#^[a-z]+://#', $url))
				{
					$filename = basename($url);
					$directory = dirname($url);
					($directory == '.') ? $directory = '' : '';
					$cid = 'cid:' . md5($filename);
					$ext = pathinfo($filename, pathinfo_extension);
					$mimetype = self::_mime_types($ext);
					if (strlen($basedir) > 1 && substr($basedir, - 1) != '/')
					{
						$basedir .= '/';
					}
					if (strlen($directory) > 1 && substr($directory, - 1) != '/')
					{
						$directory .= '/';
					}
					if ($this->addembeddedimage($basedir . $directory . $filename, md5($filename), $filename, 'base64', $mimetype))
					{
						$message = preg_replace("/" . $images[1][$i] . "=\"" . preg_quote($url, '/') . "\"/ui", $images[1][$i] . "=\"" . $cid . "\"", $message);
					}
				}
			}
		}
		$this->ishtml(true);
		$this->body = $message;
		$textmsg = trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/s', '', $message)));
		if (! empty($textmsg) && empty($this->altbody))
		{
			$this->altbody = html_entity_decode($textmsg);
		}
		if (empty($this->altbody))
		{
			$this->altbody = 'to view this email message, open it in a program that understands html!' . "\n\n";
		}
	}
	
	/**
	 * gets the mime type of the embedded or inline image
	 * @param string file extension
	 * @access public
	 * @return string mime type of ext
	 * @static
	 */
	public static function _mime_types($ext = '')
	{
		$mimes = array(
				'hqx' => 'application/mac-binhex40', 
				'cpt' => 'application/mac-compactpro', 
				'doc' => 'application/msword', 
				'bin' => 'application/macbinary', 
				'dms' => 'application/octet-stream', 
				'lha' => 'application/octet-stream', 
				'lzh' => 'application/octet-stream', 
				'exe' => 'application/octet-stream', 
				'class' => 'application/octet-stream', 
				'psd' => 'application/octet-stream', 
				'so' => 'application/octet-stream', 
				'sea' => 'application/octet-stream', 
				'dll' => 'application/octet-stream', 
				'oda' => 'application/oda', 
				'pdf' => 'application/pdf', 
				'ai' => 'application/postscript', 
				'eps' => 'application/postscript', 
				'ps' => 'application/postscript', 
				'smi' => 'application/smil', 
				'smil' => 'application/smil', 
				'mif' => 'application/vnd.mif', 
				'xls' => 'application/vnd.ms-excel', 
				'ppt' => 'application/vnd.ms-powerpoint', 
				'wbxml' => 'application/vnd.wap.wbxml', 
				'wmlc' => 'application/vnd.wap.wmlc', 
				'dcr' => 'application/x-director', 
				'dir' => 'application/x-director', 
				'dxr' => 'application/x-director', 
				'dvi' => 'application/x-dvi', 
				'gtar' => 'application/x-gtar', 
				'php' => 'application/x-httpd-php', 
				'php4' => 'application/x-httpd-php', 
				'php3' => 'application/x-httpd-php', 
				'phtml' => 'application/x-httpd-php', 
				'phps' => 'application/x-httpd-php-source', 
				'js' => 'application/x-javascript', 
				'swf' => 'application/x-shockwave-flash', 
				'sit' => 'application/x-stuffit', 
				'tar' => 'application/x-tar', 
				'tgz' => 'application/x-tar', 
				'xhtml' => 'application/xhtml+xml', 
				'xht' => 'application/xhtml+xml', 
				'zip' => 'application/zip', 
				'mid' => 'audio/midi', 
				'midi' => 'audio/midi', 
				'mpga' => 'audio/mpeg', 
				'mp2' => 'audio/mpeg', 
				'mp3' => 'audio/mpeg', 
				'aif' => 'audio/x-aiff', 
				'aiff' => 'audio/x-aiff', 
				'aifc' => 'audio/x-aiff', 
				'ram' => 'audio/x-pn-realaudio', 
				'rm' => 'audio/x-pn-realaudio', 
				'rpm' => 'audio/x-pn-realaudio-plugin', 
				'ra' => 'audio/x-realaudio', 
				'rv' => 'video/vnd.rn-realvideo', 
				'wav' => 'audio/x-wav', 
				'bmp' => 'image/bmp', 
				'gif' => 'image/gif', 
				'jpeg' => 'image/jpeg', 
				'jpg' => 'image/jpeg', 
				'jpe' => 'image/jpeg', 
				'png' => 'image/png', 
				'tiff' => 'image/tiff', 
				'tif' => 'image/tiff', 
				'css' => 'text/css', 
				'html' => 'text/html', 
				'htm' => 'text/html', 
				'shtml' => 'text/html', 
				'txt' => 'text/plain', 
				'text' => 'text/plain', 
				'log' => 'text/plain', 
				'rtx' => 'text/richtext', 
				'rtf' => 'text/rtf', 
				'xml' => 'text/xml', 
				'xsl' => 'text/xml', 
				'mpeg' => 'video/mpeg', 
				'mpg' => 'video/mpeg', 
				'mpe' => 'video/mpeg', 
				'qt' => 'video/quicktime', 
				'mov' => 'video/quicktime', 
				'avi' => 'video/x-msvideo', 
				'movie' => 'video/x-sgi-movie', 
				'doc' => 'application/msword', 
				'word' => 'application/msword', 
				'xl' => 'application/excel', 
				'eml' => 'message/rfc822');
		return (! isset($mimes[strtolower($ext)])) ? 'application/octet-stream' : $mimes[strtolower($ext)];
	}
	
	/**
	 * set (or reset) class objects (variables)
	 *
	 * usage example:
	 * $page->set('x-priority', '3');
	 *
	 * @access public
	 * @param string $name parameter name
	 * @param mixed $value parameter value
	 * note: will not work with arrays, there are no arrays to set/reset
	 * @todo should this not be using __set() magic function?
	 */
	public function set($name, $value = '')
	{
		try
		{
			if (isset($this->$name))
			{
				$this->$name = $value;
			}
			else
			{
				throw new phpmailerexception($this->lang('variable_set') . $name, self::stop_critical);
			}
		}
		catch ( exception $e )
		{
			$this->seterror($e->getmessage());
			if ($e->getcode() == self::stop_critical)
			{
				return false;
			}
		}
		return true;
	}
	
	/**
	 * strips newlines to prevent header injection.
	 * @access public
	 * @param string $str string
	 * @return string
	 */
	public function secureheader($str)
	{
		$str = str_replace("\r", '', $str);
		$str = str_replace("\n", '', $str);
		return trim($str);
	}
	
	/**
	 * set the private key file and password to sign the message.
	 *
	 * @access public
	 * @param string $key_filename parameter file name
	 * @param string $key_pass password for private key
	 */
	public function sign($cert_filename, $key_filename, $key_pass)
	{
		$this->sign_cert_file = $cert_filename;
		$this->sign_key_file = $key_filename;
		$this->sign_key_pass = $key_pass;
	}
	
	/**
	 * set the private key file and password to sign the message.
	 *
	 * @access public
	 * @param string $key_filename parameter file name
	 * @param string $key_pass password for private key
	 */
	public function dkim_qp($txt)
	{
		$tmp = "";
		$line = "";
		for($i = 0; $i < strlen($txt); $i++)
		{
			$ord = ord($txt[$i]);
			if (((0x21 <= $ord) && ($ord <= 0x3a)) || $ord == 0x3c || ((0x3e <= $ord) && ($ord <= 0x7e)))
			{
				$line .= $txt[$i];
			}
			else
			{
				$line .= "=" . sprintf("%02x", $ord);
			}
		}
		return $line;
	}
	
	/**
	 * generate dkim signature
	 *
	 * @access public
	 * @param string $s header
	 */
	public function dkim_sign($s)
	{
		$privkeystr = file_get_contents($this->dkim_private);
		if ($this->dkim_passphrase != '')
		{
			$privkey = openssl_pkey_get_private($privkeystr, $this->dkim_passphrase);
		}
		else
		{
			$privkey = $privkeystr;
		}
		if (openssl_sign($s, $signature, $privkey))
		{
			return base64_encode($signature);
		}
	}
	
	/**
	 * generate dkim canonicalization header
	 *
	 * @access public
	 * @param string $s header
	 */
	public function dkim_headerc($s)
	{
		$s = preg_replace("/\r\n\s+/", " ", $s);
		$lines = explode("\r\n", $s);
		foreach ( $lines as $key => $line )
		{
			list ( $heading, $value ) = explode(":", $line, 2);
			$heading = strtolower($heading);
			$value = preg_replace("/\s+/", " ", $value); // compress useless spaces
			$lines[$key] = $heading . ":" . trim($value); // don't forget to remove wsp around the value
		}
		$s = implode("\r\n", $lines);
		return $s;
	}
	
	/**
	 * generate dkim canonicalization body
	 *
	 * @access public
	 * @param string $body message body
	 */
	public function dkim_bodyc($body)
	{
		if ($body == '')
			return "\r\n";
			// stabilize line endings
		$body = str_replace("\r\n", "\n", $body);
		$body = str_replace("\n", "\r\n", $body);
		// end stabilize line endings
		while(substr($body, strlen($body) - 4, 4) == "\r\n\r\n")
		{
			$body = substr($body, 0, strlen($body) - 2);
		}
		return $body;
	}
	
	/**
	 * create the dkim header, body, as new header
	 *
	 * @access public
	 * @param string $headers_line header lines
	 * @param string $subject subject
	 * @param string $body body
	 */
	public function dkim_add($headers_line, $subject, $body)
	{
		$dkimsignaturetype = 'rsa-sha1'; // signature & hash algorithms
		$dkimcanonicalization = 'relaxed/simple'; // canonicalization of header/body
		$dkimquery = 'dns/txt'; // query method
		$dkimtime = time(); // signature timestamp = seconds since 00:00:00 - jan 1, 1970 (utc time zone)
		$subject_header = "subject: $subject";
		$headers = explode("\r\n", $headers_line);
		foreach ( $headers as $header )
		{
			if (strpos($header, 'from:') === 0)
			{
				$from_header = $header;
			}
			elseif (strpos($header, 'to:') === 0)
			{
				$to_header = $header;
			}
		}
		$from = str_replace('|', '=7c', $this->dkim_qp($from_header));
		$to = str_replace('|', '=7c', $this->dkim_qp($to_header));
		$subject = str_replace('|', '=7c', $this->dkim_qp($subject_header)); // copied header fields (dkim-quoted-printable
		$body = $this->dkim_bodyc($body);
		$dkimlen = strlen($body); // length of body
		$dkimb64 = base64_encode(pack("h*", sha1($body))); // base64 of packed binary sha-1 hash of body
		$ident = ($this->dkim_identity == '') ? '' : " i=" . $this->dkim_identity . ";";
		$dkimhdrs = "dkim-signature: v=1; a=" . $dkimsignaturetype . "; q=" . $dkimquery . "; l=" . $dkimlen . "; s=" . $this->dkim_selector . ";\r\n" . "\tt=" . $dkimtime . "; c=" . $dkimcanonicalization . ";\r\n" . "\th=from:to:subject;\r\n" . "\td=" . $this->dkim_domain . ";" . $ident . "\r\n" . "\tz=$from\r\n" . "\t|$to\r\n" . "\t|$subject;\r\n" . "\tbh=" . $dkimb64 . ";\r\n" . "\tb=";
		$tosign = $this->dkim_headerc($from_header . "\r\n" . $to_header . "\r\n" . $subject_header . "\r\n" . $dkimhdrs);
		$signed = $this->dkim_sign($tosign);
		return "x-phpmailer-dkim: phpmailer.sourceforge.net\r\n" . $dkimhdrs . $signed . "\r\n";
	}
	
	protected function docallback($issent, $to, $cc, $bcc, $subject, $body)
	{
		if (! empty($this->action_function) && function_exists($this->action_function))
		{
			$params = array(
					$issent, 
					$to, 
					$cc, 
					$bcc, 
					$subject, 
					$body);
			call_user_func_array($this->action_function, $params);
		}
	}
	
	/**
	 * @author n0aX
	 * @desc Added for chainable magic call to set up parameters
	 * @param string $name
	 * @param string $arguments
	 * @version $Id: 20120110
	 */

	public function __call($name, $arguments) {

		if (substr($name,0,4) == 'set_') {

			$property = substr($name,4);
			$this->$property = $arguments[0];

		} 
		
		if (substr($name,0,4) == 'add_') {

			$property = substr($name,4);
			array_push($this->$property, $arguments[0]);
		}

		return $this;
	}
	
}

class phpmailerexception extends exception
{
	public function errormessage()
	{
		$errormsg = '<strong>' . $this->getmessage() . "</strong><br />\n";
		return $errormsg;
	}
}
